/**
 * This file is part of the educational usermanagement system.
 * 
 * Copyright (c) 2008, Florian Arndt, Thomas Kemptner, Christian Kerl, Patrick Neuberger, Marcel Rehfeld, Emal Sadran, Stefan Sauer
 * 
 * Released under the MIT License http://www.opensource.org/licenses/mit-license.php
 */

package edu.ba.management.data;

import java.lang.reflect.*;
import java.util.*;

import edu.ba.management.*;
import edu.ba.management.data.search.*;

/**
 * A reference implementation of the IDataStore interface.
 */
public class DataStoreImpl implements IDataStore
{
    private static IDataStore instance = null;

    /**
     * Returns the instance of the data store.
     * 
     * @return the data store instance
     */
    public static IDataStore getInstance()
    {
        if (instance == null)
        {
            instance = new DataStoreImpl();
        }

        return instance;
    }

    private HashMap<Integer, User> users;
    private HashMap<Integer, Group> groups;

    private int lastUserId = 0;
    private int lastGroupId = 0;

    /**
     * Protect constructor since it is a singleton class.
     */
    protected DataStoreImpl()
    {
        this.users = new HashMap<Integer, User>();
        this.groups = new HashMap<Integer, Group>();
    }

    /*
     * (non-Javadoc)
     * 
     * @see edu.ba.management.data.IDataStore#findGroups(edu.ba.management.data.search.FilterChain)
     */
    @Override
    public List<Group> findGroups(FilterChain filter)
            throws NoSuchFieldException
    {
        return this.cloneElements(filter.execute(this.groups.values()));
    }

    /*
     * (non-Javadoc)
     * 
     * @see edu.ba.management.data.IDataStore#findUsers(edu.ba.management.data.search.FilterChain)
     */
    @Override
    public List<User> findUsers(FilterChain filter) throws NoSuchFieldException
    {
        return this.cloneElements(filter.execute(this.users.values()));
    }

    /*
     * (non-Javadoc)
     * 
     * @see edu.ba.management.data.IDataStore#getGroupById(int)
     */
    @Override
    public Group getGroupById(int id)
    {
        try
        {
            return (Group) this.groups.get(id).clone();
        }
        catch (Exception e)
        {
            return null;
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see edu.ba.management.data.IDataStore#getGroupsByIds(java.util.List)
     */
    @Override
    public List<Group> getGroupsByIds(List<Integer> ids)
    {
        ArrayList<Group> result = new ArrayList<Group>(ids.size());

        for (int id : ids)
        {
            result.add(this.getGroupById(id));
        }

        return result;
    }

    /*
     * (non-Javadoc)
     * 
     * @see edu.ba.management.data.IDataStore#getGroups()
     */
    @Override
    public List<Group> getGroups()
    {
        return this.cloneElements(this.groups.values());
    }

    /*
     * (non-Javadoc)
     * 
     * @see edu.ba.management.data.IDataStore#getUserById(int)
     */
    @Override
    public User getUserById(int id)
    {
        try
        {
            return (User) this.users.get(id).clone();
        }
        catch (Exception e)
        {
            return null;
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see edu.ba.management.data.IDataStore#getUsersByIds(java.util.List)
     */
    @Override
    public List<User> getUsersByIds(List<Integer> ids)
    {
        ArrayList<User> result = new ArrayList<User>(ids.size());

        for (int id : ids)
        {
            result.add(this.getUserById(id));
        }

        return result;
    }

    /*
     * (non-Javadoc)
     * 
     * @see edu.ba.management.data.IDataStore#getUsers()
     */
    @Override
    public List<User> getUsers()
    {
        return this.cloneElements(this.users.values());
    }

    /*
     * (non-Javadoc)
     * 
     * @see edu.ba.management.data.IDataStore#remove(edu.ba.management.Group)
     */
    @Override
    public Group remove(Group group)
    {
        if (group != null && group.getId() != Group.INVALID_ID)
        {
            this.groups.remove(group.getId());

            for (int user_id : new ArrayList<Integer>(group.getUsers()))
            {
                this.users.get(user_id).removeGroup(group);
            }

            try
            {
                Field field = Group.class.getDeclaredField("id");
                field.setAccessible(true);
                field.set(group, Group.INVALID_ID);
            }
            catch (Exception e)
            {
            }
        }

        return group;
    }

    /*
     * (non-Javadoc)
     * 
     * @see edu.ba.management.data.IDataStore#remove(edu.ba.management.User)
     */
    @Override
    public User remove(User user)
    {
        if (user != null && user.getId() != User.INVALID_ID)
        {
            this.users.remove(user.getId());

            for (int group_id : new ArrayList<Integer>(user.getGroups()))
            {
                this.groups.get(group_id).removeUser(user);
            }

            try
            {
                Field field = User.class.getDeclaredField("id");
                field.setAccessible(true);
                field.set(user, User.INVALID_ID);
            }
            catch (Exception e)
            {
            }
        }

        return user;
    }

    /*
     * (non-Javadoc)
     * 
     * @see edu.ba.management.data.IDataStore#store(edu.ba.management.Group)
     */
    @Override
    public Group store(Group group)
    {
        if (group != null && group.getId() == Group.INVALID_ID)
        {
            try
            {
                Field field = Group.class.getDeclaredField("id");
                field.setAccessible(true);
                field.set(group, this.lastGroupId);

                this.lastGroupId++;
            }
            catch (Exception e)
            {
            }
        }

        this.groups.put(group.getId(), group);

        return group;
    }

    /*
     * (non-Javadoc)
     * 
     * @see edu.ba.management.data.IDataStore#store(edu.ba.management.User)
     */
    @Override
    public User store(User user)
    {
        if (user != null && user.getId() == User.INVALID_ID)
        {
            try
            {
                Field field = User.class.getDeclaredField("id");
                field.setAccessible(true);
                field.set(user, this.lastUserId);

                this.lastUserId++;
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }

        this.users.put(user.getId(), user);

        return user;
    }

    /**
     * Clones all elements of a collection.
     * 
     * @param <T> the type of the objects in the collection
     * @param collection the collection to clone
     * @return a list containing all cloned elements
     */
    @SuppressWarnings("unchecked")
    protected <T> List<T> cloneElements(Collection<T> collection)
    {
        ArrayList<T> result = new ArrayList<T>();

        if (!collection.isEmpty())
        {
            try
            {
                Method clone = collection.iterator().next().getClass()
                        .getMethod("clone");

                for (T obj : collection)
                {
                    result.add((T) clone.invoke(obj));
                }
            }
            catch (Exception e)
            {
            }
        }

        return result;
    }
}
